Flash以4KB为一个Page进行擦除/写入操作(如果在一个Page需要写入一个字节,需要将整个Page擦出),Flash组成部分及其它们关联的链接器文件如下。
对于协议栈以库文件方式链接的工程(_lirary):
cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。对于采用两个FLash镜像方式编译的工程(Split Image):
Application Image Code Space(应用程序镜像代码区):独立的应用程序Flash镜像,该镜像在应用程序的链接器配置文件中被配置:cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。
Stack Image Code Space(协议栈镜像代码区):独立的协议栈Flash镜像。该镜像在协议栈的链接器配置文件中被配置:cc26xx_stack.icf
(IAR)和cc26xx_ stack.cmd
(CCS)。
Simple NV (SNV)Area:非易失性存储器使用,可供应用程序和协议栈(GAP Bond Manager)使用。有关SNV的配置,请参阅本文后面的使用Simple NV进行Flash存储。配置时,SNV Flash存储区域是协议栈镜像的一部分。
Customer Configuration Area(CCA):Flash的最后一个Page用于存储客户特定芯片配置(CCFG)参数。CCA扇区未使用的空间被分配给应用程序项目。请参阅本文后面的用户配置区。
本节在系统级别介绍用于两个拆分镜像项目配置的Flash内存映射。如下图1所示,应用程序链接文件存储在用实线箭头指向的内存位置和协议栈连接文件存储在用虚线箭头指向的内存位置。
注意:将协议栈作为库的工程,协议栈以库的形式被应用程序调用,协议栈和应用程序合并放在应用程序镜像代码空间中,而不需要与拆分镜像
相关的任何边界要求。
表1.总结了图29中的Flash System Map
定义, 并提供了可在各个IDE链接器文件中找到的关联链接器的定义或符号。
表1. Flash系统映射定义
符号/区域 | 意义 | 工程 | CCS定义 | IAR定义 |
---|---|---|---|---|
APP_FLASH_START | flash的起始位置/应用程序镜像的起始位置 | App | FLASH_APP_BASE | FLASH_START |
APP_FLASH_END | 应用程序镜像的结束位置(ICALL_STACK0_ADDR) | App | ADJ_ICALL_STACK0_START_FLASH_APP_BASE | FLASH_END |
STACK_FLASH_START | 协议栈镜像的起始位置(ICALL_STACK0_ADDR) | Stack | FLASH_START | FLASH_START |
STACK_FLASH_END | 协议栈镜像的结束位置,包括SNV | Stack | FLASH_SIZE_RESERVED_SIZE | FLASH_END |
CCA sector | flash最后的扇区,包括CCFG | App | FLASH_LAST_PAGE | FLASH_LAST_PAGE |
CCFG region | 位于CCA中,用来存储本地配置参数 | App | CCA的最后88个字节 | CCA的最后88个字节 |
应用程序和协议栈代码镜像区域是基于预定义符号ICALL_STACK0_ADDR和ICALL_STACK0_START。这些值定义协议栈镜像的入口函数的硬编码Flash地址:为它本质上是应用程序和协议栈项目边界字对齐的Flash地址。了确保正确链接,应用程序和协议栈项目都必须使用相同的定义符号。默认情况下,链接器配置会将未使用的flash分配给应用程序项目,但可以通过边界工具手动或自动修改。使用边界工具配置Flash边界地址的有关信息,请参阅本文后面的边界工具操作。
Flash的Simple NV (SNV)区域用于存储不易变动的数据,例如用于绑定的加密密钥或需要存储的自定义参数。协议栈可以配置成为SNV预留最多两个4kBFlash page,尽管有效数据仅存储在一个可用的Flash page中。为了最小化Flash上的擦除周期数,当扇区具有80%的无效数据时,SNV管理器对Flash扇区(可能是多个扇区)进行压缩。压缩是将有效数据复制到临时区域,然后擦除先前存储数据的扇区。再根据OSAL_SNV Values
中描述的OSAL_SNV值,该有效数据接着会被放回新擦除的扇区中或着保存在新扇区中。可以通过在协议栈项目中设置OSAL_SNV预处理器符号的值来配置分配给SNV的Flash扇区数目。表2列出了可配置的有效值以及相关描述。
表2. OSAL_SNV值
OSAL_SNV值 | 描述 |
---|---|
0 | SNV未使能。不能在NV中存储绑定密钥。这时应用程序和协议栈工程代码的存储区域最大。这时GAP Bond Manager也不能使能。在协议栈工程中需要设置预处理符号NO_OSAL_SNV来关闭GAP Bond Manager功能。关于配置低功耗蓝牙协议栈特征可以查看Stack Configurations |
1(默认值) | 分配一个flash扇区给SNV。绑定信息存储在NV中。flash压缩时使用cache RAM作为中间存储,因此压缩期间的功率损耗将导致SNV数据丢失。此外,由于临时禁用cache,在压缩期间可能会导致系统性能的降低。在协议栈工程中设置预处理符号OSAL_SNV=1 |
2 | 分配两个flash扇区给SNV。绑定信息存储在NV区。在压缩期间功率损耗时SNV数据会受到保护 |
将OSAL_SNV设置成其他值是无效的。设置的值越低可以为应用程序或协议栈工程分配的代码空间越大。可以使用以下API读取或写入SNV。
uint8 osal_snv_read( osalSnvId_t id, osalSnvLen_t len, void *pBuf)
||从NV读取数据|
|形参|id-有效的NV条目
len-读取数据的长度
pBuf-存储读取数据的缓冲区指针|
|返回值|SUCCESS:NV条目读取成功
NV_OPER_FAILED:读取NV条目失败|
uint8 osal_snv_write(osalSnvId_t id,osalSnvLen_t len,void * pBuf)
||写数据到NV|
|形参|id-有效的NV条目
len-写入数据的长度
pBuf-包含了需要写入数据的缓冲区指针。所有数据都会被立即更新|
|返回值|SUCCESS:写入NV条目成功
NV_OPER_FAILED:写入NV条目失败|
由于SNV与BLE5-Stack中的其他模块(如GAP Bond Manager)是共享的,所以必须小心管理NV条目ID。默认情况下,客户可用的ID在bcomdef.h中有定义,如下代码所示。
1 // Device NV Items - Range 0 - 0x1F
2 #define BLE_NVID_IRK 0x02 //!< The Device's IRK
3 #define BLE_NVID_CSRK 0x03 //!< The Device's CSRK
4 #define BLE_NVID_SIGNCOUNTER 0x04 //!< The Device's Sign Counter
5 #define BLE_LRU_BOND_LIST 0x05 //!< The Device's order of bond indexes in least recently used order
6 // Bonding NV Items - Range 0x20 - 0x5F - This allows for 10 bondings
7 #define BLE_NVID_GAP_BOND_START 0x20 //!< Start of the GAP Bond Manager's NV IDs
8 #define BLE_NVID_GAP_BOND_END 0x5f //!< End of the GAP Bond Manager's NV IDs Range
9 // GATT Configuration NV Items - Range 0x70 - 0x79 - This must match the number of Bonding entries
10 #define BLE_NVID_GATT_CFG_START 0x70 //!< Start of the GATT Configuration NV IDs
11 #define BLE_NVID_GATT_CFG_END 0x79 //!< End of the GATT Configuration NV IDs
12 // Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries
13 #define BLE_NVID_CUST_START 0x80 //!< Start of the Customer's NV IDs
14 #define BLE_NVID_CUST_END 0x8F //!< End of the Customer's NV IDs
15
16
17
如下代码显示了如何从SNV flash中读取和写入一个字节数组:
/*********************************************************************
* GLOBAL VARIABLES
*/
#define BUF_LEN 4
#define SNV_ID_APP 0x80
uint8 buf[BUF_LEN] ={0,};
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
{
// Initialize application
SimpleBLEPeripheral_init();
uint8 status = SUCCESS;
status = osal_snv_read(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV READ FAIL: %d", status);
//Write first time to initialize SNV ID
osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
}
//Increment first element of array and write to SNV flash
buf[0]++;
status = osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV WRITE FAIL: %d", status);
}
else
{
Display_print1(dispHandle, 0, 0, "Num of Resets: %d", buf[0]);
}
// Application main loop
for (;;)
{
//...
}
NV条目ID不需要事先初始化; OSAL SNV管理器在首次调用osal_snv_write()成功访问SNV时会初始化NV ID。
当向SNV读取或写入大量数据时,TI建议将读/写数据放置在静态(链接器)分配的数组或从堆中分配的缓冲区中。在本地数组中放置大量数据可能会导致任务堆栈溢出。
默认情况下,osalSnvId_t
和osalSnvLen_t
被定义为uint8
类型。要定义为uint16
类型,需要在应用程序和协议栈项目中去修改预处理符号OSAL_SNV_UINT16_ID
的定义。
osal_snv_read
和osal_snv_write
只允许在任务上下文中使用。在Swis或Hwis内无法调用此API。
用户配置区(CCA)占用flash的最后一页,用户可以在用户配置(CCFG)表中配置各种芯片和系统的参数。ccfg_app_ble.c中定义了CCFG表,你可以在应用程序项目的Startup
文件夹中找到ccfg_app_ble.c文件。CCA扇区的最后88个(sizeof(ccfg_t))字节由系统为CCFG表保留。默认情况下,链接器将CCA扇区未使用的Flash分配给应用程序镜像来存储代码和数据。也可以修改链接器以保留整个扇区来存储用户参数(例如,板序列号和其他标识参数)。
CCA区域由应用程序的链接器文件中的FLASH_LAST_PAGE
定义。在IDE中展示如下:
CCS中:
MEMORY
{
...
// CCFG Page, contains .ccfg code section and some application code.
FLASH_LAST_PAGE (RX) : origin = FLASH_LAST_PAGE_START, length = FLASH_PAGE_LEN
...
}
SECTIONS
{
...
.ccfg : > FLASH_LAST_PAGE (HIGH)
...
}
IAR中:
////////////////////////////////////////////////////////////////////////////////
// Memory Regions
////////////////////////////////////////////////////////////////////////////////
...
define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE - PAGE_SIZE) to FLASH_SIZE-1];
...
////////////////////////////////////////////////////////////////////////////////
...
// CCFG
place at end of FLASH_LAST_PAGE { readonly section .ccfg };
keep { section .ccfg }
有关CCFG区域和相关配置选项的详细信息,包括如何设置CCFG以禁止对内部flash内容的访问,请参阅CC26XX技术参考手册。
与Flash类似,RAM在应用程序和协议栈工程之间也是共享。RAM部分在其各自的链接器文件中被配置。
cc26xx_app.icf
(IAR)和cc26xx_app.cmd
(CCS)。.bss
和.data
部分的RAM空间。此镜像在协议栈的链接器配置文件中被配置:cc26xx_stack.icf
(IAR)和cc26xx_stack.cmd
(CCS)。下面的图2System Memory Map展示了simple_peripheral工程默认的系统内存映射。这个图表只是一个概要,你可以在IAR中的输出文件夹或CCS中的FlashROM文件夹中的simple_peripheral_app.map和simple_peripheral_stack.map文件中找到给定编译的确切内存位置。在利用map文件查看系统flash和RAM使用情况中,应用程序链接器文件包含实心箭头指向的符号,协议栈链接器文件包含虚线箭头指向的符号。
应用程序和协议栈的RAM内存映射基于通用的宏定义符号:ICALL_RAM0_START
。该值定义了应用程序的RAM空间结束以及协议栈.BSS
和.DATA
段镜像开始的硬编码RAM边界位置。与Flash边界不同,协议栈工程的元素(如任务堆栈和堆)在应用程序项目中分配。为确保正确链接,应用程序和协议栈工程都必须使用相同的ICALL_RAM0_START
值。默认情况下,边界工具通过配置ICALL_RAM0_START
将未使用的RAM空间分配给应用程序工程。使用边界工具配置RAM边界地址的有关信息,请参阅本文后面的边界工具操作。
除了RTOS和ICall堆,考虑一下内存的其他部分。如TI-RTOS概述中的任务所述,每个任务都有自己用于上下文切换的运行时栈。运行时栈由RTOS用于main(),HWI和SWI。该系统堆栈在应用程序链接器文件中分配,放置在应用程序RAM空间的末尾。
对于IAR,此RTOS系统堆栈由CSTACK
符号定义:
////////////////////////////////////////////////////////////////////////////////
// Stack
define symbol STACK_SIZE = 0x400;
define symbol STACK_START = RAM_END + 1;
define symbol STACK_END = STACK_START - STACK_SIZE;
//
define symbol STACK_TOP = RAM_END + 1;
export symbol STACK_TOP;
// Runtime Stack
define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack };
place at end of RAM { block CSTACK };
在IAR中,要更改CSTACK
的大小,需要调整应用程序链接器文件中的STACK_SIZE
的值。
对于CCS,RTOS系统堆栈由RTOS配置文件appBLE.cfg中的Program.stack参数定义:
/* main() and Hwi, Swi stack size */
Program.stack = 1024;
并由链接器放置在应用程序的RAM空间中:
/* Create global constant that points to top of stack */
/* CCS: Change stack size under Project Properties */
__STACK_TOP = __stack + __STACK_SIZE;
系统使用两个堆进行动态内存分配。应用程序设计人员必须了解每个堆的使用情况,以便最大限度地利用可用内存。
在RTOS配置文件app_ble.cfg中,RTOS配置了一个小堆:
var HeapMem = xdc.useModule('xdc.runtime.HeapMem');
BIOS.heapSize = 1668;
该堆(HeapMem)用于初始化RTOS对象以及分配低功耗蓝牙协议栈的任务运行时堆栈。TI选择使用此大小的堆来满足系统初始化要求。由于此堆的体积本来就小,所以不建议从RTOS堆分配内存以供一般应用程序使用。有关TI-RTOS堆配置的更多信息,请参阅TI-RTOS SYS/BIOS Kernel User’s Guide中的Heap Implementations部分。
应用程序必须使用单独的堆。ICall模块使用应用程序RAM区域的一部分,这部分也可以由各种任务使用。该ICALL堆的大小由应用程序工程中的HEAPMGR_SIZE
预处理符号定义。可以用非零值将ICall堆设定为指定大小,如果HEAPMGR_SIZE
值为0,系统会自动将堆的大小设置为链接器未分配而可用的空闲RAM的大小。默认情况下,如simple_peripheral
项目使用的是自动大小分配功能。虽然ICall堆在应用程序工程中定义,但该堆由低功耗蓝牙协议栈使用。分配内存的API(如GATT_bm_alloc())是从ICall堆分配内存。
要分析ICALL堆使用的情况,需要在应用程序工程的预处理器符号中定义HEAPMGR_METRICS
。启用自动堆大小功能后要确定ICall堆的大小的有关信息,请参阅Profiling the ICall Heap Manager (heapmgr.h)。
注意:自动堆大小功能不能确定和知晓应用程序所需的堆大小。系统设计人员必须确保堆具有应用程序运行时需要的足够内存空间。
以下是使用ICALL堆动态分配可变长度(n)数组的示例:
//define pointer
uint8_t *pArray;
// Create dynamic pointer to array.
if (pArray = (uint8_t*)ICall_malloc(n*sizeof(uint8_t)))
{
//fill up array
}
else
{
//not able to allocate
}
以下是释放上一个数组空间的示例:
ICall_free(pMsg->payload);
缓存是为处理器在RAM上保留的一个8KB空间。缓存模块临时存储从Flash读取的数据,以便经常使用的数据不需要每次访问Flash来获取。这样可以减少CPU等待状态并节省电量。当不使用缓存时就不对它供电。当缓存没有工作时就让它处于待机和空闲的状态。
如果应用程序需要更多内存,或者SRAM空间,则缓存可以当作RAM使用。这样链接器就可以将编译应用程序的一部分存储在这一部分的RAM中。这部分RAM将被称为通用RAM(GPRAM)。这样做会稍微降低程序的运行速度,并且会增加休眠状态下的设备功耗。这是因为与缓存相反的GPRAM即使是在设备休眠时也必须供电。CC2640R2F数据表中列出了具有和不具有缓存的待机模式下的当前消耗,您可以去查看验证。
上面我们已经提到,将缓存作为GPRAM使用会稍微降低运行速度以及增加功耗。对于如何影响设备的功耗将取决于应用。对于某些应用,增加的功耗会非常小,但是对于处理密集型应用,增加的功耗会略高一些。您可以使用 Measuring Bluetooth Low Energy Power Consumption Application Report (SWRA478)中描述的方法来验证您应用程序的电流消耗。
把缓存作为RAM使用,需要做两件事情。首先,必须在使用这块内存的时候告诉程序是将它作为cache用还是GPRAM来用。其次,链接器必须被告知要将用作缓存的内存区域分配给GPRAM,以及哪一部分代码要存储在GPRAM中,这是在链接器的命令/配置文件中完成。在CCS和IAR中,链接器命令/配置文件的语法略有不同。要了解有关CCS链接器命令文件的更多信息,请参阅wiki文章Linker Command File Primer。要了解有关IAR链接器的更多信息,请参阅IAR C/C++ Development Guide。
在BLE5-Stack中有一些示例项目会存在一个编译配置,允许将缓存用作RAM。对于多角色工程来说是很好的。在这种情况下,选择该编译配置就可以将缓存作为RAM使用了。在CCS中:Project->Build Configurations->Set Active->FlashROM-CacheAsRAM
。在IAR中:Project->Edit Configurations->FlashROM-CacheAsRAM
。
警告:更改项目的编译配置时,工程属性/选项可能会重置。更改编译配置后,需要检查更改一些工程预定义等 。
如果要想在没有CacheAsRAM编译配置的工程中将缓存作为RAM使用,请按照下列步骤操作:
注意:CCS用户和IAR用户的步骤会不同。根据您的工程所基于的示例项目不同操作步骤也会不同。对于在ble5stack文件夹中的示例项目,只需要1-5步。
app_ble_ccfg.c
或ccfg.c
)中,在#include <startup_files/ccfg.c>
之前添加下面的代码:#ifdef CACHE_AS_RAM
#define SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM 0x0 /* Enable GPRAM */
#endif //CACHE_AS_RAM
#include <startup_files/ccfg.c>
#ifdef CACHE_AS_RAM
// retain cache during standby
Power_setConstraint(PowerCC26XX_SB_VIMS_CACHE_RETAIN);
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
#else
// Enable iCache pre-fetching
VIMSConfigure(VIMS_BASE, TRUE, TRUE);
// Enable cache
VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
#endif //CACHE_AS_RAM
警告:请确保您的程序在将缓存用作RAM时没有使用VIMS。
在同一个文件中,包括以下文件:(在ble5stack项目中,这些文件已经包含在main.c中)
/* Power Driver */
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
/* Header files required to enable instruction fetch cache */
#include <ti/devices/cc26x0r2/inc/hw_memmap.h>
#include <ti/devices/cc26x0r2/driverlib/vims.h>
3.去编译器预定义选项中添加CACHE_AS_RAM
。对于来自ble5stack文件夹的示例项目,此定义将在以下文件中对执行的代码进行更改:
CACHE_AS_RAM=1
。这个定义会对cc26xx_app.cmd/cc26xx_app.icf中的执行代码进行更改。注意:有关如何编辑编译器和链接器符号设置的更多信息,请参阅以下部分:
CCS:
访问预处理器符号
访问链接器符号
IAR:
访问预处理器符号
访问链接器符号
5.如果您的工程是基于BLE5-Stack的工程,这将使.bss(ll.o除外)从SRAM移动到GPRAM。LL.o需要由RF驱动器放置在SRAM中。其他对象也可以根据需要移动到.bss
中。您可以在本文后面的使用AUX RAM作为RAM中找到示例。重新编译并更新您的应用程序工程。查看.map
文件确定设备内存的哪些部分被占用。(或者在CCS中可以通过:View->Memory Allocation
)
6.如果您的项目不是基于BLE5-Stack的项目,要将cache用作GPRAM还需要进行相关修改。如果您的项目正在使用无线电,在无线电覆盖中添加0x00018063
。
7.GPRAM内存区域必须在链接器命令文件中定义。定义语法在CCS和IAR链接器中是不同的。下面分别说明一下:
在CCS中,链接器命令文件的尾缀是.cmd
(如:CC2640R2_LAUNCHXL_TIRTOS.cmd
)。
清单2. 在Memory Sizes
下,添加GPRAM开始地址和长度的定义。
/*******************************************************************************
* Memory Sizes
*/
#define FLASH_BASE 0x00000000
#define GPRAM_BASE 0x11000000
#define RAM_BASE 0x20000000
#define ROM_BASE 0x10000000
#ifdef CC26X0ROM
#define FLASH_SIZE 0x00020000
#define GPRAM_SIZE 0x00002000
#define RAM_SIZE 0x00005000
#define ROM_SIZE 0x0001C000
#endif /* CC26X0ROM */
清单3. 在Memory Definitions
下添加GPRAM
/*******************************************************************************
* GPRAM
*/
#ifdef CACHE_AS_RAM
#define GPRAM_START GPRAM_BASE
#define GPRAM_END (GPRAM_START + GPRAM_SIZE - 1)
#endif /* CACHE_AS_RAM */
清单4. 在MEMORY{}
中为GPRAM分配空间
#ifdef CACHE_AS_RAM
GPRAM(RWX) : origin = GPRAM_APP_BASE, length = GPRAM_SIZE
#endif /* CACHE_AS_RAM */
清单5. 在SECTIONS{}
中将.bss
从SRAM移动到GPRAM。
GROUP > SRAM
{
.data
#ifndef CACHE_AS_RAM
.bss
#endif /* CACHE_AS_RAM */
.vtable
.vtable_ram
vtable_ram
.sysmem
.nonretenvar
#ifdef CACHE_AS_RAM
}
#else // !CACHE_AS_RAM
} LOAD_END(heapStart)
#endif //CACHE_AS_RAM
.stack : > SRAM (HIGH) LOAD_START(heapEnd)
#ifdef CACHE_AS_RAM
ll_bss > SRAM
{
--library=cc2640_ll_*.a<ll.o> (.bss)
}LOAD_END(heapStart)
.bss :
{
*(.bss)
} > GPRAM
#endif /* CACHE_AS_RAM */
重新编译你的应用程序。会将.bss
从SRAM移动到GPRAM,放在自动堆大小开始的后面。其他对象也可以移动。请参阅本文后面的使用AUX RAM作为RAM查看例程。
.icf
(例如CC2640R2_LAUNCHXL_TIRTOS.icf
)。Memory Definitions
下面添加对GPRAM起始地址和长度的定义。 //////////////////////////////////////////////////////////////////////////////
// GPRAM
//
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define symbol GPRAM_START = 0x11000000;
define symbol GPRAM_SIZE = 8096;
define symbol GPRAM_END = GPRAM_START + GPRAM_SIZE;
}
清单7. 在Memory Regions
下面,为GPRAM分配空间。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define region GPRAM = mem:[from GPRAM_START to GPRAM_END];
}
清单8. 在Memory Placement
下,将.bss
从SRAM移动到GPRAM。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
// GPRAM
define block GPDATA { section .bss };
place in GPRAM { block GPDATA } except { module ll.o };
}
重新编译应用程序。就会将.bss
从SRAM移动到GPRAM。其他对象也可以移动。可以在本文后面的使用AUX RAM作为RAM查看例程。
AUX RAM是属于传感器控制器中的一个2KB的内存区域。如果应用程序不使用传感器控制器,则可以将此内存用作应用程序的RAM。但是,访问该存储器比访问SRAM要慢得多。这可能导致功耗增加以及程序执行速度变慢。
要在应用程序中将AUX RAM作为RAM使用,请按照以下步骤操作(先描述CCS,再描述IAR)。
1.在链接器命令文件中添加一个新的定义:在CCS中:Project->Properties->ARM Linker->Advanced Options->Command File Preprocessing
。在IAR中:Options->Linker->Config
。添加AUX_AS_RAM=1
。
2.链接器命令/配置文件在CCS和IAR中有些不同。这里先介绍在CCS中更改链接器配置文件,再介绍IAR。
cc26xx_app.cmd
中,定义AUX_RAM的内存区域:#ifdef AUX_AS_RAM
#define AUX_RAM_BASE 0x400E0000
#define AUX_RAM_SIZE 0x800
#endif /* AUX_AS_RAM */
MEMORY{}
中创建一个AUX_RAM内存区域#ifdef AUX_AS_RAM
AUX_RAM (RWX) : origin = AUX_RAM_BASE, length = AUX_RAM_SIZE
#endif /* AUX_AS_RAM */
SECTIONS{}
中将内存或代码的部分添加到AUX_RAM中清单9. 将目标文件移动到AUX_RAM中。示例来自simple_peripheral(cc26xx_app.cmd
)
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(.data)
devinfoservice.obj(.data)
simple_gatt_profile.obj(.data)
icall.obj(.data)
board.obj(.bss)
} > AUX_RAM
#endif/* AUX_AS_RAM */
.obj文件放在应用程序项目的FlashROM文件夹中。它们也在.map文件中列出了大小。链接器命令文件和内存部分的详细描述在维基文章Linker Command File Primer中给出。
如果你想要更多地控制AUX_RAM中存储的内容,你可以使用命令#pragma DATA_SECTION
将指定的变量存储里面。但请注意,这仅适用于全局变量。
清单10.将全局显示句柄的变量移动到AUX RAM中一个叫做my_section
的新区域。
// Display Interface
#pragma DATA_SECTION(dispHandle, "my_section")
Display_Handle dispHandle = NULL;
清单11. 在链接器命令文件(cc26xx_app.cmd)中,将这部分(my_section
)添加到AUX_RAM。
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(my_section)
} > AUX_RAM
#endif /* AUX_AS_RAM */
警告:仅对链接器命令文件进行更改时,请确保要重新编译(
Rebuild
),而不仅仅是编译(Build
)。(CCS在您重新编译之前,不会认识到您对项>目进行了更改。)
cc26xx_app.icf
。在Memory Definitions
下添加////////////////////////////////////////////////////////////////////////////////
// AUX_RAM
//
if ( isdefinedsymbol(AUX_AS_RAM) )
{
define symbol AUX_RAM_START = 0x400E0000;
define symbol AUX_RAM_SIZE = 0x800;
define symbol AUX_RAM_END = AUX_RAM_START + AUX_RAM_SIZE;
}
Memory Regions
中,添加if ( isdefinedsymbol(AUX_AS_RAM) )
{
define region AUX_RAM = mem:[from AUX_RAM_START to AUX_RAM_END];
}
Memory Placement
下添加cc26xx_app.icf
)的示例if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section .data object simple_peripheral.o,
section .data object devinfoservice.o,
section .data object simple_gatt_profile,
section .data object icall.o,
section .data object board.o};
place in AUX_RAM { block AUXDATA };
}
.o文件是在.map文件中列出的。有关链接器配置文件的更多信息,请参阅IAR C/C++ Development Guide。
如果你想要更多操控AUX_RAM中存储的内容,你可以使用命令#pragma DATA_SECTION
将指定的变量存储在里面。但请注意,这仅适用于全局变量。
清单13.将全局显示句柄的变量移动到AUX RAM中一个叫做my_section
的新区域
// Display Interface
#pragma location="my_section"
Display_Handle dispHandle = NULL;
清单14. 在链接器配置文件(cc26xx_app.cmd
)中,将下面这部分代码添加到AUX_RAM。
if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section my_section object simple_peripheral.o };
place in AUX_RAM { block AUXDATA };
}
在将应用程序和协议栈工程编译为两个镜像文件时,边界工具用来自动调整两个镜像之间共享的各个RAM和Flash的边界地址符号。应用程序将协议栈作为库使用的工程不需要使用Frontier Tool。
边界工具作为在协议栈工程编译后的一个步骤来运行,它基于对协议栈链接器和映射文件的分析来调整相应的RAM和Flash边界。边界工具不会修改任何项目文件和源代码,不运行任何编译器,也不执行链接器优化; 该工具只调整和更新位于编译器和链接器配置文件中的各个flash和RAM的边界地址,我们的应用程序以及协议栈工程都会使用这些配置文件。
边界工具安装到SDK中的以下路径:<SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\tools\frontier\frontier.exe
(<SDK_INSTALL_DIR>是蓝牙协议栈SDK的安装路径)
此工具的python源代码也包括在其中。
表3显示了边界工具更新的边界地址符号。
表3. 边界地址符号
|边界地址符号|描述|
|ICALL_STACK0_START|应用程序和协议栈之间的flash边界地址。代表应用程序镜像的结束和协议栈镜像的开始|
|ICALL_STACK0_ADDR|协议栈实体的地址(flash)|
|ICALL_RAM0_START|应用程序和协议栈之间的RAM边界地址。代表应用程序RAM的结束以及协议栈RAM的开始|
默认情况下,所有的示例应用程序项目都配置成使用边界工具; 因此,不需要进行边界工具的用户配置。当协议栈配置更改时,或者在协议栈工程中一些文件的更新导致协议栈镜像大小发生变化,边界文件可能会更新。因此在每次协议栈工程编译后都必须重新编译应用程序工程。
注意:
frontier tool替代了早期SDK中使用的边界工具。
边界工具(frontier.exe)是协议栈工程在CCS或IAR集成开发环境中编译后的进行调用的。如果需要对RAM或Flash边界进行调整,则边界工具将更新以下列出的边界链接器配置和C定义文件。要并入更新后的配置值,请在应用程序工程上执行Project -> Rebuild All
。在重新编译应用程序之前,协议栈工程必须正确编译和链接。
SDK中的每个工程都有一组配置文件,IDE的链接器和编译器利用它们设置或调整相应的Flash和RAM值。这些配置文件在应用程序和协议栈工作区之间共享,并存储在以下位置:
<SDK_INSTALL_DIR>\examples\rtos\<EVAL_BOARD>\ble5stack\<PROJECT>\tirtos\<IDE>\config
其中<EVAL_BOARD>
是评估平台,<PROJECT>
是示例应用程序(例如simple_peripheral
),<IDE>
是IAR或CCS。
例如,在CC2640R2F LaunchPad上运行的simple_peripheral
示例应用程序,边界配置文件位于以下路径:
CCS: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\ccs\config
IAR: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\iar\config
以下是边界配置文件:
iar_boundary.xcl
[IAR]或ccs_linker_defines.cmd
[CCS],为链接器定义边界地址。该文件位于TOOLS IDE文件夹中,并在需要进行调整时由边界工具进行更新。iar_boundary.bdef
[IAR]或ccs_compiler_defines.bcfg
[CCS]。定义编译器的边界地址。该文件位于TOOLS IDE文件夹中,并在需要进行调整时由边界工具进行更新。注意:边界链接器配置文件和边界C定义文件中的值必须匹配。
文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。